﻿//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2020 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Core;
namespace UnityGameFramework.Editor.ResourceTools
{
    /// <summary>
    /// 资源编辑器。
    /// </summary>
    internal sealed partial class ResourceEditor : EditorWindow
    {
        private ResourceEditorController m_Controller;
        private MenuState m_MenuState = MenuState.Normal;
        private MenuState m_AssetListState = MenuState.Normal;
        private Resource m_SelectedResource;
        private ResourceFolder m_ResourceRoot;
        private HashSet<string> m_ExpandedResourceFolderNames;
        private HashSet<Asset> m_SelectedAssetsInSelectedResource;
        private HashSet<Folder> m_SelectedFoldersInSelectedResource;
        private HashSet<SourceFolder> m_ExpandedSourceFolders;
        private HashSet<SourceAsset> m_SelectedSourceAssets;
        private HashSet<SourceFolder> m_SelectedSourceFolders;
        private Texture m_MissingSourceAssetIcon;

        private Vector2 m_ResourcesViewScroll = Vector2.zero;
        private Vector2 m_ResourceViewScroll = Vector2.zero;
        private Vector2 m_SourceAssetsViewScroll = Vector2.zero;
        private string m_InputResourceName;
        private string m_InputResourceVariant;
        private string m_InputResourceFileSystem;
        private bool m_HideAssignedSourceAssets;
        private int m_CurrentResourceContentCount;
        private int m_CurrentResourceRowOnDraw;
        private int m_CurrentSourceRowOnDraw;

        private string m_SearchResourceName;
        private string m_SearchResourceVariant;
        private HashSet<string> m_SearchPathList = new HashSet<string>();
        private string m_SearchTag = "";
        private bool m_SearchTextureFlag;
        private bool m_SearchMeshFlag;
        private bool m_SearchMaterialFlag;

        public static Resource SelectedResource;

        [MenuItem("ThirdParty Tools/Game Framework/Resource Tools/Resource Editor", false, 42)]
        private static void Open()
        {
            ResourceEditor window = GetWindow<ResourceEditor>("Resource Editor", true);
            window.minSize = new Vector2(1400f, 600f);
        }

        private void OnEnable()
        {
            m_Controller = new ResourceEditorController();
            m_Controller.OnLoadingResource += OnLoadingResource;
            m_Controller.OnLoadingAsset += OnLoadingAsset;
            m_Controller.OnLoadCompleted += OnLoadCompleted;
            m_Controller.OnAssetAssigned += OnAssetAssigned;
            m_Controller.OnAssetUnassigned += OnAssetUnassigned;
            m_Controller.OnFolderAssigned += OnFolderAssigned;
            m_Controller.OnFolderUnassigned += OnFolderUnassigned;


            m_MenuState = MenuState.Normal;
            m_AssetListState = MenuState.Normal;
            m_SelectedResource = null;
            m_ResourceRoot = new ResourceFolder("Resources", null);
            m_ExpandedResourceFolderNames = new HashSet<string>();
            m_SelectedAssetsInSelectedResource = new HashSet<Asset>();
            m_SelectedFoldersInSelectedResource = new HashSet<Folder>();
            m_ExpandedSourceFolders = new HashSet<SourceFolder>();
            m_SelectedSourceAssets = new HashSet<SourceAsset>();
            m_SelectedSourceFolders = new HashSet<SourceFolder>();
            m_MissingSourceAssetIcon = EditorGUIUtility.IconContent("console.warnicon.sml").image;

            m_ResourcesViewScroll = Vector2.zero;
            m_ResourceViewScroll = Vector2.zero;
            m_SourceAssetsViewScroll = Vector2.zero;
            m_InputResourceName = null;
            m_InputResourceVariant = null;
            m_InputResourceFileSystem = null;
            m_HideAssignedSourceAssets = false;
            m_CurrentResourceContentCount = 0;
            m_CurrentResourceRowOnDraw = 0;
            m_CurrentSourceRowOnDraw = 0;

            if (m_Controller.Load())
            {
                Debug.Log("Load configuration success.");
            }
            else
            {
                Debug.LogWarning("Load configuration failure.");
            }

            EditorUtility.DisplayProgressBar("Prepare Resource Editor", "Processing...", 0f);
            RefreshResourceTree();
            EditorUtility.ClearProgressBar();
        }

        private void OnGUI()
        {
            EditorGUILayout.BeginHorizontal(GUILayout.Width(position.width), GUILayout.Height(position.height));
            {
                GUILayout.Space(2f);
                EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.25f));
                {
                    GUILayout.Space(5f);
                    EditorGUILayout.LabelField(Utils.Text.Format("Resource List ({0})", m_Controller.ResourceCount.ToString()), EditorStyles.boldLabel);
                    EditorGUILayout.BeginHorizontal("box", GUILayout.Height(position.height - 68f));
                    {
                        DrawResourcesView();
                    }
                    EditorGUILayout.EndHorizontal();

                    EditorGUILayout.BeginHorizontal();
                    {
                        GUILayout.Space(5f);
                        DrawResourcesMenu();
                    }
                    EditorGUILayout.EndHorizontal();
                }
                EditorGUILayout.EndVertical();
                EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.35f));
                {
                    GUILayout.Space(5f);
                    EditorGUILayout.LabelField(Utils.Text.Format("Resource Content ({0})", m_CurrentResourceContentCount.ToString()), EditorStyles.boldLabel);
                    EditorGUILayout.BeginHorizontal("box", GUILayout.Height(position.height - 52f));
                    {
                        DrawResourceView();
                    }
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.BeginHorizontal();
                    {
                        GUILayout.Space(5f);
                        DrawResourceMenu();
                    }
                    EditorGUILayout.EndHorizontal();
                }
                EditorGUILayout.EndVertical();
                EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.4f - 16f));
                {
                    GUILayout.Space(5f);
                    EditorGUILayout.LabelField("Asset List", EditorStyles.boldLabel);
                    EditorGUILayout.BeginHorizontal("box", GUILayout.Height(position.height - 52f));
                    {
                        DrawSourceAssetsView();
                    }
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.BeginHorizontal();
                    {
                        GUILayout.Space(5f);
                        if (m_AssetListState == MenuState.Normal)
                        {
                            DrawSourceAssetsMenu();
                        }
                        else
                        {
                            DrawSearchField();
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                }
                EditorGUILayout.EndVertical();
                GUILayout.Space(5f);
            }
            EditorGUILayout.EndHorizontal();
        }

        private void DrawResourcesView()
        {
            m_CurrentResourceRowOnDraw = 0;
            m_ResourcesViewScroll = EditorGUILayout.BeginScrollView(m_ResourcesViewScroll);
            {
                DrawResourceFolder(m_ResourceRoot);
            }
            EditorGUILayout.EndScrollView();
        }

        private void DrawResourceFolder(ResourceFolder folder)
        {
            bool expand = IsExpandedResourceFolder(folder);
            EditorGUILayout.BeginHorizontal();
            {
#if UNITY_2019_3_OR_NEWER
                bool foldout = EditorGUI.Foldout(new Rect(18f + 14f * folder.Depth, 20f * m_CurrentResourceRowOnDraw + 4f, int.MaxValue, 14f), expand, string.Empty, true);
#else
                bool foldout = EditorGUI.Foldout(new Rect(18f + 14f * folder.Depth, 20f * m_CurrentResourceRowOnDraw + 2f, int.MaxValue, 14f), expand, string.Empty, true);
#endif
                if (expand != foldout)
                {
                    expand = !expand;
                    SetExpandedResourceFolder(folder, expand);
                }

#if UNITY_2019_3_OR_NEWER
                GUI.DrawTexture(new Rect(32f + 14f * folder.Depth, 20f * m_CurrentResourceRowOnDraw + 3f, 16f, 16f), ResourceFolder.Icon);
                EditorGUILayout.LabelField(string.Empty, GUILayout.Width(44f + 14f * folder.Depth), GUILayout.Height(18f));
#else
                GUI.DrawTexture(new Rect(32f + 14f * folder.Depth, 20f * m_CurrentResourceRowOnDraw + 1f, 16f, 16f), ResourceFolder.Icon);
                EditorGUILayout.LabelField(string.Empty, GUILayout.Width(40f + 14f * folder.Depth), GUILayout.Height(18f));
#endif
                EditorGUILayout.LabelField(folder.Name);
            }
            EditorGUILayout.EndHorizontal();

            m_CurrentResourceRowOnDraw++;

            if (expand)
            {
                foreach (ResourceFolder subFolder in folder.GetFolders())
                {
                    DrawResourceFolder(subFolder);
                }

                foreach (ResourceItem resourceItem in folder.GetItems())
                {
                    DrawResourceItem(resourceItem);
                }
            }
        }

        private void DrawResourceItem(ResourceItem resourceItem)
        {
            EditorGUILayout.BeginHorizontal();
            {
                string title = resourceItem.Name;
                if (resourceItem.Resource.Packed)
                {
                    title = "[Packed] " + title;
                }
                if (resourceItem.Resource.SingleBuild)
                {
                    title = "[SingleBuild] " + title;
                }

                float emptySpace = position.width;
                if (EditorGUILayout.Toggle(m_SelectedResource == resourceItem.Resource, GUILayout.Width(emptySpace - 12f)))
                {
                    ChangeSelectedResource(resourceItem.Resource);
                }
                else if (m_SelectedResource == resourceItem.Resource)
                {
                    ChangeSelectedResource(null);
                }

                GUILayout.Space(-emptySpace + 24f);
#if UNITY_2019_3_OR_NEWER
                GUI.DrawTexture(new Rect(32f + 14f * resourceItem.Depth, 20f * m_CurrentResourceRowOnDraw + 3f, 16f, 16f), resourceItem.Icon);
                EditorGUILayout.LabelField(string.Empty, GUILayout.Width(30f + 14f * resourceItem.Depth), GUILayout.Height(18f));
#else
                GUI.DrawTexture(new Rect(32f + 14f * resourceItem.Depth, 20f * m_CurrentResourceRowOnDraw + 1f, 16f, 16f), resourceItem.Icon);
                EditorGUILayout.LabelField(string.Empty, GUILayout.Width(26f + 14f * resourceItem.Depth), GUILayout.Height(18f));
#endif
                EditorGUILayout.LabelField(title);
            }
            EditorGUILayout.EndHorizontal();
            m_CurrentResourceRowOnDraw++;
        }

        private void DrawResourcesMenu()
        {
            switch (m_MenuState)
            {
                case MenuState.Normal:
                    DrawResourcesMenu_Normal();
                    break;

                case MenuState.Add:
                    DrawResourcesMenu_Add();
                    break;

                case MenuState.Rename:
                    DrawResourcesMenu_Rename();
                    break;

                case MenuState.Remove:
                    DrawResourcesMenu_Remove();
                    break;
            }
        }

        private void DrawResourcesMenu_Normal()
        {
            if (GUILayout.Button("Add", GUILayout.Width(65f)))
            {
                m_MenuState = MenuState.Add;
                m_InputResourceName = null;
                m_InputResourceVariant = null;
                GUI.FocusControl(null);
            }
            EditorGUI.BeginDisabledGroup(m_SelectedResource == null);
            {
                if (GUILayout.Button("Rename", GUILayout.Width(65f)))
                {
                    m_MenuState = MenuState.Rename;
                    m_InputResourceName = m_SelectedResource != null ? m_SelectedResource.Name : null;
                    m_InputResourceVariant = m_SelectedResource != null ? m_SelectedResource.Variant : null;
                    GUI.FocusControl(null);
                }
                if (GUILayout.Button("Remove", GUILayout.Width(65f)))
                {
                    m_MenuState = MenuState.Remove;
                }
                bool packed = EditorGUILayout.ToggleLeft("Packed", m_SelectedResource != null && m_SelectedResource.Packed, GUILayout.Width(65f));
                if (m_SelectedResource != null && packed != m_SelectedResource.Packed)
                {
                    SetResourcePacked(packed);
                }
                bool singleBuild = EditorGUILayout.ToggleLeft("SingleBuild", m_SelectedResource != null && m_SelectedResource.SingleBuild, GUILayout.Width(65f));
                if (m_SelectedResource != null && singleBuild != m_SelectedResource.SingleBuild)
                {
                    SetSingleBuildTag(singleBuild);
                }
            }
            EditorGUI.EndDisabledGroup();
            if (GUILayout.Button("ClearTag", GUILayout.Width(100f)))
            {
                foreach (ResourceFolder subFolder in m_ResourceRoot.GetFolders())
                {
                    ClearResourceSingleBuildTag(subFolder);
                }

                foreach (ResourceItem resourceItem in m_ResourceRoot.GetItems())
                {
                    resourceItem.Resource.SingleBuild = false;
                }
            }
        }
        private void ClearResourceSingleBuildTag(ResourceFolder f)
        {
            foreach (ResourceFolder subF in f.GetFolders())
            {
                ClearResourceSingleBuildTag(subF);
            }
            foreach (ResourceItem item in f.GetItems())
            {
                item.Resource.SingleBuild = false;
            }
        }

        private void DrawResourcesMenu_Add()
        {
            GUI.SetNextControlName("NewResourceNameTextField");
            m_InputResourceName = EditorGUILayout.TextField(m_InputResourceName);
            GUI.SetNextControlName("NewResourceVariantTextField");
            m_InputResourceVariant = EditorGUILayout.TextField(m_InputResourceVariant, GUILayout.Width(60f));

            if (GUI.GetNameOfFocusedControl() == "NewResourceNameTextField" || GUI.GetNameOfFocusedControl() == "NewResourceVariantTextField")
            {
                if (Event.current.isKey && Event.current.keyCode == KeyCode.Return)
                {
                    EditorUtility.DisplayProgressBar("Add Resource", "Processing...", 0f);
                    AddResource(m_InputResourceName, m_InputResourceVariant, true);
                    EditorUtility.ClearProgressBar();
                    Repaint();
                }
            }

            if (GUILayout.Button("Add", GUILayout.Width(50f)))
            {
                EditorUtility.DisplayProgressBar("Add Resource", "Processing...", 0f);
                AddResource(m_InputResourceName, m_InputResourceVariant, true);
                EditorUtility.ClearProgressBar();
            }

            if (GUILayout.Button("Back", GUILayout.Width(50f)))
            {
                m_MenuState = MenuState.Normal;
            }
        }

        private void DrawResourcesMenu_Rename()
        {
            if (m_SelectedResource == null)
            {
                m_MenuState = MenuState.Normal;
                return;
            }

            GUI.SetNextControlName("RenameResourceNameTextField");
            m_InputResourceName = EditorGUILayout.TextField(m_InputResourceName);
            GUI.SetNextControlName("RenameResourceVariantTextField");
            m_InputResourceVariant = EditorGUILayout.TextField(m_InputResourceVariant, GUILayout.Width(60f));

            if (GUI.GetNameOfFocusedControl() == "RenameResourceNameTextField" || GUI.GetNameOfFocusedControl() == "RenameResourceVariantTextField")
            {
                if (Event.current.isKey && Event.current.keyCode == KeyCode.Return)
                {
                    EditorUtility.DisplayProgressBar("Rename Resource", "Processing...", 0f);
                    RenameResource(m_SelectedResource, m_InputResourceName, m_InputResourceVariant);
                    EditorUtility.ClearProgressBar();
                    Repaint();
                }
            }

            if (GUILayout.Button("OK", GUILayout.Width(50f)))
            {
                EditorUtility.DisplayProgressBar("Rename Resource", "Processing...", 0f);
                RenameResource(m_SelectedResource, m_InputResourceName, m_InputResourceVariant);
                EditorUtility.ClearProgressBar();
            }

            if (GUILayout.Button("Back", GUILayout.Width(50f)))
            {
                m_MenuState = MenuState.Normal;
            }
        }

        private void DrawResourcesMenu_Remove()
        {
            if (m_SelectedResource == null)
            {
                m_MenuState = MenuState.Normal;
                return;
            }

            GUILayout.Label(Utils.Text.Format("Remove '{0}' ?", m_SelectedResource.FullName));

            if (GUILayout.Button("Yes", GUILayout.Width(50f)))
            {
                EditorUtility.DisplayProgressBar("Remove Resource", "Processing...", 0f);
                RemoveResource();
                EditorUtility.ClearProgressBar();
                m_MenuState = MenuState.Normal;
            }

            if (GUILayout.Button("No", GUILayout.Width(50f)))
            {
                m_MenuState = MenuState.Normal;
            }
        }

        private void DrawResourceView()
        {
            m_ResourceViewScroll = EditorGUILayout.BeginScrollView(m_ResourceViewScroll);
            {
                if (m_SelectedResource != null)
                {
                    int index = 0;

                    Folder[] folders = m_Controller.GetFolders(m_SelectedResource.Name, m_SelectedResource.Variant);
                    Asset[] assets = m_Controller.GetAssets(m_SelectedResource.Name, m_SelectedResource.Variant);

                    m_CurrentResourceContentCount = assets.Length + folders.Length;

                    foreach (var folder in folders)
                    {

                        SourceFolder sourceFolder = m_Controller.GetSourceFolder(folder.Path);
                        string path = sourceFolder.FromRootPath;
                        EditorGUILayout.BeginHorizontal();
                        {
                            float emptySpace = position.width;
                            bool select = IsSelectedFolderInSelectedResource(folder);
                            if (select != EditorGUILayout.Toggle(select, GUILayout.Width(emptySpace - 12f)))
                            {
                                select = !select;
                                SetSelectedFolderInSelectedResource(folder, select);
                            }

                            GUILayout.Space(-emptySpace + 24f);
#if UNITY_2019_3_OR_NEWER
                            // GUI.DrawTexture(new Rect(20f, 20f * index++ + 3f, 16f, 16f), sourceAsset != null ? sourceAsset.Icon : m_MissingSourceAssetIcon);
                            EditorGUILayout.LabelField(string.Empty, GUILayout.Width(16f), GUILayout.Height(18f));
#else
                            GUI.DrawTexture(new Rect(20f, 20f * index++ + 1f, 16f, 16f), SourceFolder.Icon);
                            EditorGUILayout.LabelField(string.Empty, GUILayout.Width(14f), GUILayout.Height(18f));
#endif
                            EditorGUILayout.LabelField(path);
                        }
                        EditorGUILayout.EndHorizontal();
                    }

                    foreach (Asset asset in assets)
                    {
                        SourceAsset sourceAsset = m_Controller.GetSourceAsset(asset.Guid);
                        string assetName = sourceAsset != null ? (m_Controller.AssetSorter == AssetSorterType.Path ? sourceAsset.Path : (m_Controller.AssetSorter == AssetSorterType.Name ? sourceAsset.Name : sourceAsset.Guid)) : asset.Guid;
                        EditorGUILayout.BeginHorizontal();
                        {
                            float emptySpace = position.width;
                            bool select = IsSelectedAssetInSelectedResource(asset);
                            if (select != EditorGUILayout.Toggle(select, GUILayout.Width(emptySpace - 12f)))
                            {
                                select = !select;
                                SetSelectedAssetInSelectedResource(asset, select);
                            }

                            GUILayout.Space(-emptySpace + 24f);
#if UNITY_2019_3_OR_NEWER
                            GUI.DrawTexture(new Rect(20f, 20f * index++ + 3f, 16f, 16f), sourceAsset != null ? sourceAsset.Icon : m_MissingSourceAssetIcon);
                            EditorGUILayout.LabelField(string.Empty, GUILayout.Width(16f), GUILayout.Height(18f));
#else
                            GUI.DrawTexture(new Rect(20f, 20f * index++ + 1f, 16f, 16f), sourceAsset != null ? sourceAsset.Icon : m_MissingSourceAssetIcon);
                            EditorGUILayout.LabelField(string.Empty, GUILayout.Width(14f), GUILayout.Height(18f));
#endif
                            EditorGUILayout.LabelField(assetName);
                        }
                        EditorGUILayout.EndHorizontal();
                    }
                }
                else
                {
                    m_CurrentResourceContentCount = 0;
                }
            }
            EditorGUILayout.EndScrollView();
        }

        private void DrawResourceMenu()
        {
            if (GUILayout.Button("All", GUILayout.Width(50f)) && m_SelectedResource != null)
            {
                Asset[] assets = m_Controller.GetAssets(m_SelectedResource.Name, m_SelectedResource.Variant);
                Folder[] folders = m_Controller.GetFolders(m_SelectedResource.Name, m_SelectedResource.Variant);
                foreach (Asset asset in assets)
                {
                    SetSelectedAssetInSelectedResource(asset, true);
                }
                foreach (var folder in folders)
                {
                    SetSelectedFolderInSelectedResource(folder, true);
                }
            }
            if (GUILayout.Button("None", GUILayout.Width(50f)))
            {
                m_SelectedAssetsInSelectedResource.Clear();
                m_SelectedFoldersInSelectedResource.Clear();
            }
            m_Controller.AssetSorter = (AssetSorterType)EditorGUILayout.EnumPopup(m_Controller.AssetSorter, GUILayout.Width(60f));
            GUILayout.Label(string.Empty);
            EditorGUI.BeginDisabledGroup(m_SelectedResource == null || (m_SelectedAssetsInSelectedResource.Count + m_SelectedFoldersInSelectedResource.Count) <= 0);
            {
                if (GUILayout.Button(Utils.Text.Format("{0}|{1} >>", m_SelectedAssetsInSelectedResource.Count, m_SelectedFoldersInSelectedResource.Count), GUILayout.Width(80f)))
                {
                    foreach (Asset asset in m_SelectedAssetsInSelectedResource)
                    {
                        UnassignAsset(asset);
                    }

                    m_SelectedAssetsInSelectedResource.Clear();

                    foreach (Folder folder in m_SelectedFoldersInSelectedResource)
                    {
                        UnassignFolder(folder);
                    }

                    m_SelectedFoldersInSelectedResource.Clear();
                }
            }
            EditorGUI.EndDisabledGroup();
        }

        private void DrawSourceAssetsView()
        {
            m_CurrentSourceRowOnDraw = 0;
            m_SourceAssetsViewScroll = EditorGUILayout.BeginScrollView(m_SourceAssetsViewScroll);
            {
                DrawSourceFolder(m_Controller.SourceAssetRoot, false);
            }
            EditorGUILayout.EndScrollView();
        }

        private void DrawSourceAssetsMenu()
        {
            var selectedSourceAssets = GetSelectedSourceAssets();
            var selectedSourceFolders = GetSelectSourceFolder();
            EditorGUI.BeginDisabledGroup((selectedSourceAssets.Count + selectedSourceFolders.Count) <= 0);
            {
                if (GUILayout.Button(Utils.Text.Format("<< {0}|{1}", selectedSourceAssets.Count.ToString(), selectedSourceFolders.Count), GUILayout.Width(100f)))
                {
                    if (m_SelectedResource != null)
                    {
                        foreach (var sourceAsset in selectedSourceAssets)
                        {
                            AssignAsset(sourceAsset, m_SelectedResource);
                        }

                        foreach (var sourceFolder in selectedSourceFolders)
                        {
                            AssignFolder(sourceFolder, m_SelectedResource);
                        }
                    }
                    else
                    {
                        EditorUtility.DisplayProgressBar("Add Resource", "Processing...", 0f);

                        foreach (var asset in selectedSourceAssets)
                        {
                            string[] names = asset.Name.Split('.');
                            if (names.Length < 1)
                            {
                                continue;
                            }
                            var name = $"Z_Temp/{names[0]}";
                            AddResource(name, null, false);
                            var bundle = m_Controller.GetResource(name, null);
                            AssignAsset(asset, bundle);
                        }
                        EditorUtility.ClearProgressBar();

                        RefreshResourceTree();
                    }

                    m_SelectedSourceAssets.Clear();
                    foreach (var item in m_SelectedSourceFolders)
                    {
                        item.IsHited = false;
                    }
                    m_SelectedSourceFolders.Clear();
                }
            }
            EditorGUI.EndDisabledGroup();

            bool hideAssignedSourceAssets = EditorGUILayout.ToggleLeft("Hide Assigned", m_HideAssignedSourceAssets, GUILayout.Width(100f));
            if (hideAssignedSourceAssets != m_HideAssignedSourceAssets)
            {
                m_HideAssignedSourceAssets = hideAssignedSourceAssets;
            }
            bool textureFlag = EditorGUILayout.ToggleLeft("Texture", m_SearchTextureFlag, GUILayout.Width(80f));
            if (textureFlag != m_SearchTextureFlag)
            {
                m_SearchTextureFlag = textureFlag;
                if (m_SearchTextureFlag == true)
                {
                    m_SearchTag = " t:texture";
                    m_SearchMeshFlag = false;
                    m_SearchMaterialFlag = false;
                }
            }
            bool meshFlag = EditorGUILayout.ToggleLeft("Mesh", m_SearchMeshFlag, GUILayout.Width(50f));
            if (meshFlag != m_SearchMeshFlag)
            {
                m_SearchMeshFlag = meshFlag;
                if (m_SearchMeshFlag == true)
                {
                    m_SearchTag = " t:mesh";
                    m_SearchTextureFlag = false;
                    m_SearchMaterialFlag = false;
                }
            }
            bool materialFlag = EditorGUILayout.ToggleLeft("Material", m_SearchMaterialFlag, GUILayout.Width(80f));
            if (materialFlag != m_SearchMaterialFlag)
            {
                m_SearchMaterialFlag = materialFlag;
                if (m_SearchMaterialFlag == true)
                {
                    m_SearchTag = " t:material";
                    m_SearchTextureFlag = false;
                    m_SearchMeshFlag = false;
                }
            }

            GUILayout.Label(string.Empty);
            if (GUILayout.Button("Search", GUILayout.Width(80f)))
            {
                m_AssetListState = MenuState.Add;
                m_SearchResourceName = null;
                GUI.FocusControl(null);
            }
            if (GUILayout.Button("Clean", GUILayout.Width(80f)))
            {
                EditorUtility.DisplayProgressBar("Clean", "Processing...", 0f);
                CleanResource();
                EditorUtility.ClearProgressBar();
            }
            if (GUILayout.Button("Save", GUILayout.Width(80f)))
            {
                EditorUtility.DisplayProgressBar("Save", "Processing...", 0f);
                SaveConfiguration();
                EditorUtility.ClearProgressBar();
            }
        }

        private void DrawSourceFolder(SourceFolder sourceFolder, bool disable)
        {
            if (m_HideAssignedSourceAssets && IsAssignedSourceFolder(sourceFolder))
            {
                return;
            }

            var is_assigned = false;
            Folder folder = m_Controller.GetFolder(sourceFolder.FromRootPath);
            if (folder != null)
            {
                is_assigned = true;
            }

            bool expand = IsExpandedSourceFolder(sourceFolder);
            EditorGUI.BeginDisabledGroup(disable);
            {
                EditorGUILayout.BeginHorizontal();
                {
                    var hitted = sourceFolder.IsHited;
                    //var isAssigned = 

                    bool select = IsSelectedSourceFolder(sourceFolder);
                    if (select != EditorGUILayout.Toggle(select, GUILayout.Width(12f + 14f * sourceFolder.Depth)))
                    {
                        if (hitted == true || is_assigned == true)
                        {
                            return;
                        }
                        select = !select;
                        SetSelectedSourceFolder(sourceFolder, select);
                    }

                    GUI.color = Color.yellow;
                    if (hitted != EditorGUILayout.Toggle(hitted, GUILayout.Width(12f)))
                    {
                        hitted = !hitted;
                        SetHitedSourceFolder(sourceFolder, hitted);

                    }
                    GUI.color = Color.white;

                    GUILayout.Space(-14f * sourceFolder.Depth);

#if UNITY_2019_3_OR_NEWER
                    bool foldout = EditorGUI.Foldout(new Rect(18f + 14f * sourceFolder.Depth, 20f * m_CurrentSourceRowOnDraw + 4f, int.MaxValue, 14f), expand, string.Empty, true);
#else
                    bool foldout = EditorGUI.Foldout(new Rect(18f + 14f * sourceFolder.Depth + 15f, 20f * m_CurrentSourceRowOnDraw + 2f, int.MaxValue, 14f), expand, string.Empty, true);
#endif
                    if (expand != foldout)
                    {
                        expand = !expand;
                        SetExpandedSourceFolder(sourceFolder, expand);
                    }

#if UNITY_2019_3_OR_NEWER
                    GUI.DrawTexture(new Rect(32f + 14f * sourceFolder.Depth, 20f * m_CurrentSourceRowOnDraw + 3f, 16f, 16f), SourceFolder.Icon);
                    EditorGUILayout.LabelField(string.Empty, GUILayout.Width(30f + 14f * sourceFolder.Depth), GUILayout.Height(18f));
#else
                    GUI.DrawTexture(new Rect(32f + 14f * sourceFolder.Depth + 15f, 20f * m_CurrentSourceRowOnDraw + 1f, 16f, 16f), SourceFolder.Icon);
                    EditorGUILayout.LabelField(string.Empty, GUILayout.Width(26f + 14f * sourceFolder.Depth), GUILayout.Height(18f));
#endif

                    EditorGUILayout.LabelField(sourceFolder.Name);

                    EditorGUILayout.LabelField(folder != null ? GetResourceFullName(folder.Resource.Name, folder.Resource.Variant) : string.Empty, GUILayout.Width(position.width * 0.15f));

                }
                EditorGUILayout.EndHorizontal();
            }
            EditorGUI.EndDisabledGroup();
            m_CurrentSourceRowOnDraw++;

            if (disable == false && (sourceFolder.IsHited == true || is_assigned == true))
            {
                disable = true;
            }

            if (expand)
            {
                foreach (SourceFolder subSourceFolder in sourceFolder.GetFolders())
                {
                    DrawSourceFolder(subSourceFolder, disable);
                }

                foreach (SourceAsset sourceAsset in sourceFolder.GetAssets())
                {
                    DrawSourceAsset(sourceAsset, disable);
                }
            }
        }

        private void DrawSourceAsset(SourceAsset sourceAsset, bool disable)
        {
            if (m_HideAssignedSourceAssets && IsAssignedSourceAsset(sourceAsset))
            {
                return;
            }
            EditorGUI.BeginDisabledGroup(disable);
            {

                EditorGUILayout.BeginHorizontal();
                {
                    float emptySpace = position.width;
                    bool select = IsSelectedSourceAsset(sourceAsset);
                    if (select != EditorGUILayout.Toggle(select, GUILayout.Width(emptySpace - 12f)))
                    {
                        select = !select;
                        SetSelectedSourceAsset(sourceAsset, select);
                    }

                    GUILayout.Space(-emptySpace + 24f + 12f);
#if UNITY_2019_3_OR_NEWER
                    GUI.DrawTexture(new Rect(32f + 14f * sourceAsset.Depth, 20f * m_CurrentSourceRowOnDraw + 3f, 16f, 16f), sourceAsset.Icon);
                    EditorGUILayout.LabelField(string.Empty, GUILayout.Width(30f + 14f * sourceAsset.Depth), GUILayout.Height(18f));
#else
                    GUI.DrawTexture(new Rect(32f + 14f * sourceAsset.Depth + 12f, 20f * m_CurrentSourceRowOnDraw + 1f, 16f, 16f), sourceAsset.Icon);
                    EditorGUILayout.LabelField(string.Empty, GUILayout.Width(26f + 14f * sourceAsset.Depth), GUILayout.Height(18f));
#endif
                    EditorGUILayout.LabelField(sourceAsset.Name);
                    Asset asset = m_Controller.GetAsset(sourceAsset.Guid);
                    EditorGUILayout.LabelField(asset != null ? GetResourceFullName(asset.Resource.Name, asset.Resource.Variant) : string.Empty, GUILayout.Width(position.width * 0.15f));
                }
                EditorGUILayout.EndHorizontal();
            }

            EditorGUI.EndDisabledGroup();

            m_CurrentSourceRowOnDraw++;
        }

        private void ChangeSelectedResource(Resource resource)
        {
            if (m_SelectedResource == resource)
            {
                return;
            }


            SelectedResource = m_SelectedResource = resource;
            m_SelectedAssetsInSelectedResource.Clear();
            m_SelectedFoldersInSelectedResource.Clear();
            m_InputResourceFileSystem = m_SelectedResource != null ? m_SelectedResource.FileSystem : null;
        }

        private void SaveConfiguration()
        {
            if (m_Controller.Save())
            {
                Debug.Log("Save configuration success.");
            }
            else
            {
                Debug.LogWarning("Save configuration failure.");
            }
        }

        private void AddResource(string name, string variant, bool refresh)
        {
            if (variant == string.Empty)
            {
                variant = null;
            }

            string fullName = GetResourceFullName(name, variant);
            if (m_Controller.AddResource(name, variant, null, LoadType.LoadFromFile, false))
            {
                if (refresh)
                {
                    RefreshResourceTree();
                }

                Debug.Log(Utils.Text.Format("Add resource '{0}' success.", fullName));
                m_MenuState = MenuState.Normal;
            }
            else
            {
                Debug.LogWarning(Utils.Text.Format("Add resource '{0}' failure.", fullName));
            }
        }

        private void RenameResource(Resource resource, string newName, string newVariant)
        {
            if (resource == null)
            {
                Debug.LogWarning("Resource is invalid.");
                return;
            }

            if (newVariant == string.Empty)
            {
                newVariant = null;
            }

            string oldFullName = resource.FullName;
            string newFullName = GetResourceFullName(newName, newVariant);
            if (m_Controller.RenameResource(resource.Name, resource.Variant, newName, newVariant))
            {
                RefreshResourceTree();
                Debug.Log(Utils.Text.Format("Rename resource '{0}' to '{1}' success.", oldFullName, newFullName));
                m_MenuState = MenuState.Normal;
            }
            else
            {
                Debug.LogWarning(Utils.Text.Format("Rename resource '{0}' to '{1}' failure.", oldFullName, newFullName));
            }
        }

        private void RemoveResource()
        {
            string fullName = m_SelectedResource.FullName;
            if (m_Controller.RemoveResource(m_SelectedResource.Name, m_SelectedResource.Variant))
            {
                ChangeSelectedResource(null);
                RefreshResourceTree();
                Debug.Log(Utils.Text.Format("Remove resource '{0}' success.", fullName));
            }
            else
            {
                Debug.LogWarning(Utils.Text.Format("Remove resource '{0}' failure.", fullName));
            }
        }

        private void SetResourceLoadType(LoadType loadType)
        {
            string fullName = m_SelectedResource.FullName;
            if (m_Controller.SetResourceLoadType(m_SelectedResource.Name, m_SelectedResource.Variant, loadType))
            {
                Debug.Log(Utils.Text.Format("Set resource '{0}' load type to '{1}' success.", fullName, loadType.ToString()));
            }
            else
            {
                Debug.LogWarning(Utils.Text.Format("Set resource '{0}' load type to '{1}' failure.", fullName, loadType.ToString()));
            }
        }

        private void SetResourcePacked(bool packed)
        {
            string fullName = m_SelectedResource.FullName;
            if (m_Controller.SetResourcePacked(m_SelectedResource.Name, m_SelectedResource.Variant, packed))
            {
                Debug.Log(Utils.Text.Format("{1} resource '{0}' success.", fullName, packed ? "Pack" : "Unpack"));
            }
            else
            {
                Debug.LogWarning(Utils.Text.Format("{1} resource '{0}' failure.", fullName, packed ? "Pack" : "Unpack"));
            }
        }
        private void SetSingleBuildTag(bool b)
        {
            string fullName = m_SelectedResource.FullName;
            if (m_Controller.SetSingleBuildTag(m_SelectedResource.Name, m_SelectedResource.Variant, b))
            {
                Debug.Log(Utils.Text.Format("{1} resource '{0}' success.", fullName, b ? "AddTag" : "Didn't AddTag"));
            }
            else
            {
                Debug.LogWarning(Utils.Text.Format("{1} resource '{0}' failure.", fullName, b ? "AddTag" : "Didn't AddTag"));
            }
        }

        private void AssignAsset(SourceAsset sourceAsset, Resource resource)
        {
            if (!m_Controller.AssignAsset(sourceAsset.Guid, sourceAsset.Path, resource.Name, resource.Variant))
            {
                Debug.LogWarning(Utils.Text.Format("Assign asset '{0}' to resource '{1}' failure.", sourceAsset.Name, resource.FullName));
            }
        }

        private void UnassignAsset(Asset asset)
        {
            if (!m_Controller.UnassignAsset(asset.Guid))
            {
                Debug.LogWarning(Utils.Text.Format("Unassign asset '{0}' from resource '{1}' failure.", asset.Guid, m_SelectedResource.FullName));
            }
        }

        private void AssignFolder(SourceFolder sourceFolder, Resource resource)
        {
            if (!m_Controller.AssignFolder(sourceFolder.FromRootPath, resource.Name, resource.Variant))
            {
                Debug.LogWarning(Utils.Text.Format("Assign folder '{0}' to resource '{1}' failure.", sourceFolder.FromRootPath, resource.FullName));
            }
        }

        private void UnassignFolder(Folder folder)
        {
            if (!m_Controller.UnAssignFolder(folder.Path))
            {
                Debug.LogWarning(Utils.Text.Format("Assign folder '{0}' to resource '{1}' failure.", folder.Path, m_SelectedResource.FullName));
            }
        }

        private void CleanResource()
        {
            int unknownAssetCount = m_Controller.RemoveUnknownAssets();
            int unusedResourceCount = m_Controller.RemoveUnusedResources();
            RefreshResourceTree();

            Debug.Log(Utils.Text.Format("Clean complete, {0} unknown assets and {1} unused resources has been removed.", unknownAssetCount.ToString(), unusedResourceCount.ToString()));
        }

        private void RefreshResourceTree()
        {
            m_ResourceRoot.Clear();
            Resource[] resources = m_Controller.GetResources();
            foreach (Resource resource in resources)
            {
                string[] splitedPath = resource.Name.Split('/');
                ResourceFolder folder = m_ResourceRoot;
                for (int i = 0; i < splitedPath.Length - 1; i++)
                {
                    ResourceFolder subFolder = folder.GetFolder(splitedPath[i]);
                    folder = subFolder == null ? folder.AddFolder(splitedPath[i]) : subFolder;
                }

                string fullName = resource.Variant != null ? Utils.Text.Format("{0}.{1}", splitedPath[splitedPath.Length - 1], resource.Variant) : splitedPath[splitedPath.Length - 1];
                folder.AddItem(fullName, resource);
            }
        }

        private bool IsExpandedResourceFolder(ResourceFolder folder)
        {
            return m_ExpandedResourceFolderNames.Contains(folder.FromRootPath);
        }

        private void SetExpandedResourceFolder(ResourceFolder folder, bool expand)
        {
            if (expand)
            {
                m_ExpandedResourceFolderNames.Add(folder.FromRootPath);
            }
            else
            {
                m_ExpandedResourceFolderNames.Remove(folder.FromRootPath);
            }
        }

        private bool IsSelectedAssetInSelectedResource(Asset asset)
        {
            return m_SelectedAssetsInSelectedResource.Contains(asset);
        }

        private void SetSelectedAssetInSelectedResource(Asset asset, bool select)
        {
            if (select)
            {
                m_SelectedAssetsInSelectedResource.Add(asset);
            }
            else
            {
                m_SelectedAssetsInSelectedResource.Remove(asset);
            }
        }

        private bool IsSelectedFolderInSelectedResource(Folder folder)
        {
            return m_SelectedFoldersInSelectedResource.Contains(folder);
        }

        private void SetSelectedFolderInSelectedResource(Folder folder, bool select)
        {
            if (select)
            {
                m_SelectedFoldersInSelectedResource.Add(folder);
            }
            else
            {
                m_SelectedFoldersInSelectedResource.Remove(folder);
            }
        }

        private bool IsExpandedSourceFolder(SourceFolder sourceFolder)
        {
            return m_ExpandedSourceFolders.Contains(sourceFolder);
        }

        private void SetExpandedSourceFolder(SourceFolder sourceFolder, bool expand)
        {
            if (expand)
            {
                m_ExpandedSourceFolders.Add(sourceFolder);
            }
            else
            {
                m_ExpandedSourceFolders.Remove(sourceFolder);
            }
        }

        private bool IsSelectedSourceFolder(SourceFolder sourceFolder)
        {
            foreach (SourceAsset sourceAsset in sourceFolder.GetAssets())
            {
                if (m_HideAssignedSourceAssets && IsAssignedSourceAsset(sourceAsset))
                {
                    continue;
                }

                if (!IsSelectedSourceAsset(sourceAsset))
                {
                    return false;
                }
            }

            foreach (SourceFolder subSourceFolder in sourceFolder.GetFolders())
            {
                if (m_HideAssignedSourceAssets && IsAssignedSourceFolder(sourceFolder))
                {
                    continue;
                }

                if (!IsSelectedSourceFolder(subSourceFolder))
                {
                    return false;
                }
            }

            return true;
        }

        private void SetHitedSourceFolder(SourceFolder folder, bool hit)
        {
            folder.IsHited = hit;
            if (hit == true)
            {
                m_SelectedSourceFolders.Add(folder);
            }
            else
            {
                m_SelectedSourceFolders.Remove(folder);
            }
        }

        private void SetSelectedSourceFolder(SourceFolder sourceFolder, bool select)
        {
            foreach (SourceAsset sourceAsset in sourceFolder.GetAssets())
            {
                if (m_HideAssignedSourceAssets && IsAssignedSourceAsset(sourceAsset))
                {
                    continue;
                }

                SetSelectedSourceAsset(sourceAsset, select);
            }

            foreach (SourceFolder subSourceFolder in sourceFolder.GetFolders())
            {
                if (m_HideAssignedSourceAssets && IsAssignedSourceFolder(subSourceFolder))
                {
                    continue;
                }

                SetSelectedSourceFolder(subSourceFolder, select);
            }
        }

        private bool IsSelectedSourceAsset(SourceAsset sourceAsset)
        {
            return m_SelectedSourceAssets.Contains(sourceAsset);
        }

        private void SetSelectedSourceAsset(SourceAsset sourceAsset, bool select)
        {
            if (select)
            {
                m_SelectedSourceAssets.Add(sourceAsset);
            }
            else
            {
                m_SelectedSourceAssets.Remove(sourceAsset);
            }
        }

        private bool IsAssignedSourceAsset(SourceAsset sourceAsset)
        {
            return m_Controller.GetAsset(sourceAsset.Guid) != null;
        }

        private bool IsAssignedSourceFolder(SourceFolder sourceFolder)
        {
            if (m_Controller.GetFolder(sourceFolder.FromRootPath) != null)
            {
                return true;
            }

            foreach (SourceAsset sourceAsset in sourceFolder.GetAssets())
            {
                if (!IsAssignedSourceAsset(sourceAsset))
                {
                    return false;
                }
            }

            foreach (SourceFolder subSourceFolder in sourceFolder.GetFolders())
            {
                if (!IsAssignedSourceFolder(subSourceFolder))
                {
                    return false;
                }
            }

            return true;
        }

        private HashSet<SourceAsset> GetSelectedSourceAssets()
        {
            if (!m_HideAssignedSourceAssets)
            {
                return m_SelectedSourceAssets;
            }

            HashSet<SourceAsset> selectedUnassignedSourceAssets = new HashSet<SourceAsset>();
            foreach (SourceAsset sourceAsset in m_SelectedSourceAssets)
            {
                if (!IsAssignedSourceAsset(sourceAsset))
                {
                    selectedUnassignedSourceAssets.Add(sourceAsset);
                }
            }

            return selectedUnassignedSourceAssets;
        }

        private HashSet<SourceFolder> GetSelectSourceFolder()
        {
            if (!m_HideAssignedSourceAssets)
            {
                return m_SelectedSourceFolders;
            }
            HashSet<SourceFolder> selectedUnassignedSourceFolders = new HashSet<SourceFolder>();
            foreach (SourceFolder sourceFolder in m_SelectedSourceFolders)
            {
                if (!IsAssignedSourceFolder(sourceFolder))
                {
                    selectedUnassignedSourceFolders.Add(sourceFolder);
                }
            }

            return selectedUnassignedSourceFolders;
        }

        private string GetResourceFullName(string name, string variant)
        {
            return variant != null ? Utils.Text.Format("{0}.{1}", name, variant) : name;
        }

        private void OnLoadingResource(int index, int count)
        {
            EditorUtility.DisplayProgressBar("Loading Resources", Utils.Text.Format("Loading resources, {0}/{1} loaded.", index.ToString(), count.ToString()), (float)index / count);
        }

        private void OnLoadingAsset(int index, int count)
        {
            EditorUtility.DisplayProgressBar("Loading Assets", Utils.Text.Format("Loading assets, {0}/{1} loaded.", index.ToString(), count.ToString()), (float)index / count);
        }

        private void OnLoadCompleted()
        {
            EditorUtility.ClearProgressBar();
        }

        private void OnAssetAssigned(SourceAsset[] sourceAssets)
        {
            Debug.Log($"OnAssetAssigned assets.cnt = {sourceAssets.Length}");
        }

        private void OnAssetUnassigned(SourceAsset[] sourceAssets)
        {
            Debug.Log($"OnAssetUnassigned assets.cnt = {sourceAssets.Length}");
        }

        private void OnFolderAssigned(SourceFolder[] folders)
        {
            Debug.Log($"OnFolderAssigned folders.cnt = {folders.Length}");
        }

        private void OnFolderUnassigned(SourceFolder[] folders)
        {
            Debug.Log($"OnFolderUnassigned folders.cnt = {folders.Length}");
        }

        private void DrawSearchField()
        {
            GUI.SetNextControlName("SearchResourceNameTextField");
            m_SearchResourceName = EditorGUILayout.TextField(m_SearchResourceName);
            GUI.SetNextControlName("SearchResourceVariantTextField");
            m_SearchResourceVariant = EditorGUILayout.TextField(m_SearchResourceVariant, GUILayout.Width(60f));

            if (GUI.GetNameOfFocusedControl() == "SearchResourceNameTextField" || GUI.GetNameOfFocusedControl() == "SearchResourceVariantTextField")
            {
                if (Event.current.isKey && Event.current.keyCode == KeyCode.Return)
                {
                    EditorUtility.DisplayProgressBar("Search Resource", "Processing...", 0f);
                    SelectSearchedSource(m_Controller.SourceAssetRoot);
                    EditorUtility.ClearProgressBar();
                }
            }

            if (GUILayout.Button("OK", GUILayout.Width(50f)))
            {
                EditorUtility.DisplayProgressBar("Search Resource", "Processing...", 0f);
                if (string.IsNullOrWhiteSpace(m_SearchResourceName) == true)
                {
                    m_ExpandedSourceFolders.Clear();
                    m_SearchPathList.Clear();
                    m_SelectedSourceAssets.Clear();
                    EditorUtility.ClearProgressBar();
                    return;
                }
                SelectSearchedSource(m_Controller.SourceAssetRoot);
                EditorUtility.ClearProgressBar();
            }

            if (GUILayout.Button("Back", GUILayout.Width(50f)))
            {
                m_AssetListState = MenuState.Normal;
            }
        }

        private void SelectSearchedSource(SourceFolder sourceFolder)
        {
            m_ExpandedSourceFolders.Clear();
            m_SearchPathList.Clear();
            m_SelectedSourceAssets.Clear();
            var guids = AssetDatabase.FindAssets(m_SearchResourceName + m_SearchTag, new string[] { "Assets/Project" });
            foreach (var guid in guids)
            {
                var path = AssetDatabase.GUIDToAssetPath(guid);
                m_SearchPathList.Add(path);
            }

            if (m_SearchPathList.Count <= 0)
            {
                Debug.LogError("Find No Asset! assetName = " + m_SearchResourceName);
                return;
            }

            SetExpandedSourceFolder(m_Controller.SourceAssetRoot, true);
            foreach (var path in m_SearchPathList)
            {
                var split = path.Split('/');
                var curfolder = sourceFolder;
                for (int i = 1; i < split.Length; ++i)
                {
                    var name = split[i];
                    if (curfolder.GetFolder(name) != null)
                    {
                        curfolder = curfolder.GetFolder(name);
                        SetExpandedSourceFolder(curfolder, true);
                    }
                    if (curfolder.GetAsset(name) != null)
                    {
                        SetSelectedSourceAsset(curfolder.GetAsset(name), true);
                    }
                }
            }
            m_AssetListState = MenuState.Normal;
        }
    }
}
